\chapter{零基础深度学习笔记：循环网络}
	\section{循环神经网络的原理}
	循环神经网络可以画成下面这个样子，
		\begin{figure}[H]\centering
		\includegraphics[scale=0.5]{xh1-0.jpg}
	\end{figure}
这个网络在t时刻接收到输入$ x_t $之后，隐藏层的值是$ s_t $，输出值是$ o_t $。关键一点是，$ s_t $的值不仅仅取决于$ x_t $，还取决于$ s_{t-1} $。我们可以用下面的公式来表示循环神经网络的计算方法：
\begin{align}\label{xh1-1}
\mathrm{o}_t&=g(V\mathrm{s}_t)\\\label{xh1-2}
\mathrm{s}_t&=f(U\mathrm{x}_t+W\mathrm{s}_{t-1})
\end{align}

式\eqref{xh1-1}是输出层的计算公式，输出层是一个全连接层，也就是它的每个节点都和隐藏层的每个节点相连。$V$是输出层的权重矩阵，$g$是激活函数。式\eqref{xh1-2}是隐藏层的计算公式，它是循环层。$U$是输入$x$的权重矩阵，$W$是上一次的值$ s_{t-1} $作为这一次的输入的权重矩阵，$f$是激活函数。

从上面的公式我们可以看出，循环层和全连接层的区别就是循环层多了一个权重矩阵 $W$。

如果反复把式\eqref{xh1-2}带入到式\eqref{xh1-1}，我们将得到：
\begin{align*}
\mathrm{o}_t&=g(V\mathrm{s}_t)\\
&=Vf(U\mathrm{x}_t+W\mathrm{s}_{t-1})\\
&=Vf(U\mathrm{x}_t+Wf(U\mathrm{x}_{t-1}+W\mathrm{s}_{t-2}))\\
&=Vf(U\mathrm{x}_t+Wf(U\mathrm{x}_{t-1}+Wf(U\mathrm{x}_{t-2}+W\mathrm{s}_{t-3})))\\
&=Vf(U\mathrm{x}_t+Wf(U\mathrm{x}_{t-1}+Wf(U\mathrm{x}_{t-2}+Wf(U\mathrm{x}_{t-3}+...))))
\end{align*}

从上面可以看出，循环神经网络的输出值$ o_t $，是受前面历次输入值$ x_t,x_{t-1},x_{t-2},\cdots $影响的，这就是为什么循环神经网络可以往前看任意多个输入值的原因。
	\section{循环神经网络的训练}
	BPTT算法是针对循环层的训练算法，它的基本原理和BP算法是一样的，也包含同样的三个步骤：
	\begin{itemize}
		\item 	前向计算每个神经元的输出值；
		\item 反向计算每个神经元的误差项$ \delta_j $值：它是误差函数$E$对神经元$j$的加权输入$ net_j $的偏导数；
\item 计算每个权重的梯度。		
	\end{itemize}
	最后再用随机梯度下降算法更新权重。
	
	循环层如下图所示：
	\begin{figure}[H]\centering
		\includegraphics[scale=0.8]{xh1-1.png}
	\end{figure}
\subsection{前向计算}
使用前面的式\eqref{xh1-2}对循环层进行前向计算：
\[\bm{s}_t=f(U\bm{x}_t+W\bm{s}_{t-1})\]

注意，上面的$ \bm{s}_t,\bm{x}_t,\bm{x}_{t-1} $是向量，用黑体字母表示；而U、V是矩阵，用大写字母表示。向量的下标表示时刻，例如，$ \bm{s}_t $表示在$t$时刻向量$\bm{s}$的值。

我们假设输入向量$\bm{x}$的维度是$m$，输出向量$\bm{s}$的维度是$n$，则矩阵U的维度是$ n\times m $，矩阵W的维度是$ n\times n $。下面是上式展开成矩阵的样子，看起来更直观一些：
\begin{align*}
\begin{bmatrix}
s_1^t\\
s_2^t\\
.\\.\\
s_n^t\\
\end{bmatrix}=f\left(
\begin{bmatrix}
u_{11}& u_{12}& ... &u_{1m}\\
u_{21}& u_{22}& ... &u_{2m}\\
.\\.\\
u_{n1} &u_{n2}& ...& u_{nm}\\
\end{bmatrix}
\begin{bmatrix}
x_1\\
x_2\\
.\\.\\
x_m\\
\end{bmatrix}+
\begin{bmatrix}
w_{11}& w_{12}& ...& w_{1n}\\
w_{21}& w_{22}& ... &w_{2n}\\
.\\.\\
w_{n1} &w_{n2}& ...& w_{nn}\\
\end{bmatrix}
\begin{bmatrix}
s_1^{t-1}\\
s_2^{t-1}\\
.\\.\\
s_n^{t-1}\\
\end{bmatrix}\right)
\end{align*}

其中，$ \bm{s}_j^t $表示向量$\bm{s}$的第$j$个元素在$t$时刻的值。$ u_{ji} $表示输入层第$i$个神经元到循环层第$j$个神经元的权重。$ w_{ji} $表示循环层第$t-1$时刻的第$i$个神经元到循环层第$t$个时刻的第$j$个神经元的权重。
\subsection{误差项$ \delta_j $的计算}
BTPP算法将第$l$层$t$时刻的误差项$ \delta_t^l $值沿两个方向传播，一个方向是其传递到上一层网络，得到$ \delta_t^{l-1} $，这部分只和权重矩阵$U$有关；另一个是方向是将其沿时间线传递到初始时刻$ t_1 $，得到$ \delta_1^l $，这部分只和权重矩阵$W$有关。
\subsubsection{时间轴上的反向传播}
我们用向量$ \bm{net}_t $表示神经元在$t$时刻的加权输入，因为：
\begin{align*}
\bm{net}_t&=U\bm{x}_t+W\bm{s}_{t-1}\\
\bm{s}_{t-1}&=f(\bm{net}_{t-1})\\
\end{align*}

因此，

\[\frac{\partial{\bm{net}_t}}{\partial{\bm{net}_{t-1}}}=\frac{\partial{\bm{net}_t}}{\partial{\bm{s}_{t-1}}}\frac{\partial{\bm{s}_{t-1}}}{\partial{\bm{net}_{t-1}}}\]
我们用$\bm{a}$表示列向量，用表示$ \bm{a}^T $行向量。上式的第一项是向量函数对向量求导，其结果为Jacobian矩阵：
\begin{align*}
\frac{\partial{\bm{net}_t}}{\partial{\bm{s}_{t-1}}}&=
\begin{bmatrix}
\frac{\partial{net_1^t}}{\partial{s_1^{t-1}}}& \frac{\partial{net_1^t}}{\partial{s_2^{t-1}}}& ...&  \frac{\partial{net_1^t}}{\partial{s_n^{t-1}}}\\
\frac{\partial{net_2^t}}{\partial{s_1^{t-1}}}& \frac{\partial{net_2^t}}{\partial{s_2^{t-1}}}& ...&  \frac{\partial{net_2^t}}{\partial{s_n^{t-1}}}\\
&.\\&.\\
\frac{\partial{net_n^t}}{\partial{s_1^{t-1}}}& \frac{\partial{net_n^t}}{\partial{s_2^{t-1}}}& ...&  \frac{\partial{net_n^t}}{\partial{s_n^{t-1}}}\\
\end{bmatrix}=\begin{bmatrix}
w_{11} & w_{12} & ... & w_{1n}\\
w_{21} & w_{22} & ... & w_{2n}\\
&.\\&.\\
w_{n1} & w_{n2} & ... & w_{nn}\\
\end{bmatrix}=W
\end{align*}
同理，上式第二项也是一个Jacobian矩阵：
\begin{align*}
\frac{\partial{\bm{s}_{t-1}}}{\partial{\bm{net}_{t-1}}}&=
\begin{bmatrix}
\frac{\partial{s_1^{t-1}}}{\partial{net_1^{t-1}}}& \frac{\partial{s_1^{t-1}}}{\partial{net_2^{t-1}}}& ...&  \frac{\partial{s_1^{t-1}}}{\partial{net_n^{t-1}}}\\
\frac{\partial{s_2^{t-1}}}{\partial{net_1^{t-1}}}& \frac{\partial{s_2^{t-1}}}{\partial{net_2^{t-1}}}& ...&  \frac{\partial{s_2^{t-1}}}{\partial{net_n^{t-1}}}\\
&.\\&.\\
\frac{\partial{s_n^{t-1}}}{\partial{net_1^{t-1}}}& \frac{\partial{s_n^{t-1}}}{\partial{net_2^{t-1}}}& ...&  \frac{\partial{s_n^{t-1}}}{\partial{net_n^{t-1}}}\\
\end{bmatrix}=\begin{bmatrix}
f'(net_1^{t-1}) & 0 & ... & 0\\
0 & f'(net_2^{t-1}) & ... & 0\\
&.\\&.\\
0 & 0 & ... & f'(net_n^{t-1})\\
\end{bmatrix}=diag[f'(\bm{net}_{t-1})]
\end{align*}
最后，将两项合在一起，可得：
\begin{align*}
\frac{\partial{\bm{net}_t}}{\partial{\bm{net}_{t-1}}}&=\frac{\partial{\bm{net}_t}}{\partial{\bm{s}_{t-1}}}\frac{\partial{\bm{s}_{t-1}}}{\partial{\bm{net}_{t-1}}}\\
&=W\cdot diag[f'(\bm{net}_{t-1})]\\
&=\begin{bmatrix}
w_{11}f'(net_1^{t-1}) & w_{12}f'(net_2^{t-1}) & ... & w_{1n}f(net_n^{t-1})\\
w_{21}f'(net_1^{t-1}) & w_{22} f'(net_2^{t-1}) & ... & w_{2n}f(net_n^{t-1})\\
&.\\&.\\
w_{n1}f'(net_1^{t-1}) & w_{n2} f'(net_2^{t-1}) & ... & w_{nn} f'(net_n^{t-1})\\
\end{bmatrix}
\end{align*}
上式描述了将$ \delta $沿时间往前传递一个时刻的规律，有了这个规律，我们就可以求得任意时刻$k$的误差项$ \delta_k $：
\begin{align}\nonumber
\delta_k^T=&\frac{\partial{E}}{\partial{\bm{net}_k}}\\\nonumber
=&\frac{\partial{E}}{\partial{\bm{net}_t}}\frac{\partial{\bm{net}_t}}{\partial{\bm{net}_k}}\\\nonumber
=&\frac{\partial{E}}{\partial{\bm{net}_t}}\frac{\partial{\bm{net}_t}}{\partial{\bm{net}_{t-1}}}\frac{\partial{\bm{net}_{t-1}}}{\partial{\bm{net}_{t-2}}}...\frac{\partial{\bm{net}_{k+1}}}{\partial{\bm{net}_{k}}}\\\nonumber
=&\delta_t^T\cdot (W\cdot diag[f'(\bm{net}_{t-1})])
(W\cdot diag[f'(\bm{net}_{t-2})])
\cdots
(W\cdot diag[f'(\bm{net}_{k})])
\\\label{xh1-3}
=&\delta_t^T\prod_{i=k}^{t-1}W\cdot diag[f'(\bm{net}_{i})]
\end{align}
\subsubsection{往上一层的传播}
循环层将误差项反向传递到上一层网络，与普通的全连接层是完全一样的，这在前面的文章《零基础入门深度学习(3) - 神经网络和反向传播算法》中已经详细讲过了，在此仅简要描述一下。

循环层的加权输入$ \bm{net}^l $与上一层的加权输入$ \bm{net}^{l-1} $关系如下：
\begin{align*}
\bm{net}_t^l=&U\bm{a}_t^{l-1}+W\bm{s}_{t-1}\\
\bm{a}_t^{l-1}=&f^{l-1}(\bm{net}_t^{l-1})
\end{align*}

其中，$ \bm{net}^l_t $是第$l$层神经元的加权输入(假设第$l$层是循环层)；$ \bm{net}^{l-1}_t $是第$l-1$层神经元的加权输入；$  \bm{a}^l_t $是第$l-1$层神经元的输出；$ f^{l-1} $是第l-1层的激活函数，那么依链式法则，有，
\begin{align*}
\frac{\partial{\bm{net}_t^l}}{\partial{\bm{net}_t^{l-1}}}=&\frac{\partial{\bm{net}^l}}{\partial{\bm{a}_t^{l-1}}}\frac{\partial{\bm{a}_t^{l-1}}}{\partial{\bm{net}_t^{l-1}}}\\
=&U\cdot diag[f'^{l-1}(\bm{net}_t^{l-1})]
\end{align*}

所以，
\begin{align}\nonumber
(\delta_t^{l-1})^T:=&\frac{\partial{E}}{\partial{\bm{net}_t^{l-1}}}\\\nonumber
=&\frac{\partial{E}}{\partial{\bm{net}_t^l}}\frac{\partial{\bm{net}_t^l}}{\partial{\bm{net}_t^{l-1}}}\\\label{xh1-4}
=&(\delta_t^l)^TU\cdot diag[f'^{l-1}(\bm{net}_t^{l-1})]
\end{align}


式\eqref{xh1-4}就是将误差项传递到上一层算法。它也是一个循环递进的，只要有了最后一层的$ (\delta_t^l)^T $，就可以递进往前计算$ (\delta_t^{l-1})^T,(\delta_t^{l-2})^T,\cdots $
\subsection{权重梯度的计算}

现在，我们终于来到了BPTT算法的最后一步：计算每个权重的梯度。
\subsubsection{$ W $的梯度计算}
首先，我们计算误差函数$E$对权重矩阵$W$的梯度$ \frac{\partial E}{\partial W} $。
\begin{figure}[H]\centering
\includegraphics[scale=0.5]{xh1-2.png}
\end{figure}
上图展示了我们到目前为止，在前两步中已经计算得到的量，包括每个时刻$ t $ 循环层的输出值$ s_t $，以及误差项$ \delta_t $。

回忆一下我们在文章《零基础入门深度学习(3) - 神经网络和反向传播算法》介绍的全连接网络的权重梯度计算算法：只要知道了任意一个时刻的误差项$ \delta_t $，以及上一个时刻循环层的输出值$ s_{t-1} $，就可以按照下面的公式求出权重矩阵在$t$时刻的梯度$ \nabla_{W_t}E $：
\begin{equation}\label{xh1-5}
\nabla_{W_t}E=\begin{bmatrix}	\delta_1^ts_1^{t-1} & \delta_1^ts_2^{t-1} & ... &  \delta_1^ts_n^{t-1}\\
\delta_2^ts_1^{t-1} & \delta_2^ts_2^{t-1} & ... &  \delta_2^ts_n^{t-1}\\
.\\.\\
\delta_n^ts_1^{t-1} & \delta_n^ts_2^{t-1} & ... &  \delta_n^ts_n^{t-1}\\
\end{bmatrix}
\end{equation}


在式\eqref{xh1-5}中，$ \delta_i^t $表示$t$时刻误差项向量的第$i$个分量；$ s_i^{t-1} $表示$t-1$时刻循环层第$i$个神经元的输出值。我们下面可以简单推导一下式\eqref{xh1-5}。

我们知道：
\[\bm{net}_t=U\bm{x}_t+W\bm{s}_{t-1}\]

矩阵表达为，
\[
\begin{bmatrix}
net_1^t\\
net_2^t\\
.\\.\\
net_n^t\\
\end{bmatrix}=U\bm{x}_t+
\begin{bmatrix}
w_{11} & w_{12} & ... & w_{1n}\\
w_{21} & w_{22} & ... & w_{2n}\\
.\\.\\
w_{n1} & w_{n2} & ... & w_{nn}\\
\end{bmatrix}
\begin{bmatrix}
s_1^{t-1}\\
s_2^{t-1}\\
.\\.\\
s_n^{t-1}\\
\end{bmatrix}\\
=U\bm{x}_t+
\begin{bmatrix}
w_{11}s_1^{t-1}+w_{12}s_2^{t-1}...w_{1n}s_n^{t-1}\\
w_{21}s_1^{t-1}+w_{22}s_2^{t-1}...w_{2n}s_n^{t-1}\\
.\\.\\
w_{n1}s_1^{t-1}+w_{n2}s_2^{t-1}...w_{nn}s_n^{t-1}\\
\end{bmatrix}\\
\]

因为对$W$求导与$ U\bm{x}_t $无关，我们不再考虑。现在，我们考虑对权重项$ w_{ji} $求导。通过观察上式我们可以看到$ w_{ji} $只与$ net_j^t $有关，所以：
\begin{align*}
\frac{\partial{E}}{\partial{w_{ji}}}=&\frac{\partial{E}}{\partial{net_j^t}}\frac{\partial{net_j^t}}{\partial{w_{ji}}}\\
=&\delta_j^ts_i^{t-1}
\end{align*}

按照上面的规律就可以生成式\eqref{xh1-5}里面的矩阵。

我们已经求得了权重矩阵W在t时刻的梯度$\nabla_{W_t}E$，最终的梯度$ \nabla_{W}E $是各个时刻的梯度之和：
\begin{align}\nonumber
\nabla_WE=&\sum_{i=1}^t\nabla_{W_i}E\\\label{xh1-6}
=&\begin{bmatrix}
\delta_1^ts_1^{t-1} & \delta_1^ts_2^{t-1} & ... &  \delta_1^ts_n^{t-1}\\
\delta_2^ts_1^{t-1} & \delta_2^ts_2^{t-1} & ... &  \delta_2^ts_n^{t-1}\\
.\\.\\
\delta_n^ts_1^{t-1} & \delta_n^ts_2^{t-1} & ... &  \delta_n^ts_n^{t-1}\\
\end{bmatrix}
+...+
\begin{bmatrix}
\delta_1^1s_1^0 & \delta_1^1s_2^0 & ... &  \delta_1^1s_n^0\\
\delta_2^1s_1^0 & \delta_2^1s_2^0 & ... &  \delta_2^1s_n^0\\
.\\.\\
\delta_n^1s_1^0 & \delta_n^1s_2^0 & ... &  \delta_n^1s_n^0\\
\end{bmatrix}
\end{align}


式\eqref{xh1-6}就是计算循环层权重矩阵$W$的梯度的公式。
\subsubsection{U的梯度计算}
同权重矩阵$W$类似，我们可以得到权重矩阵$U$的计算方法。
\begin{equation}\label{xh1-7}
\nabla_{U_t}E=\begin{bmatrix}	\delta_1^tx_1^t & \delta_1^tx_2^t & ... &  \delta_1^tx_m^t\\
\delta_2^tx_1^t & \delta_2^tx_2^t & ... &  \delta_2^tx_m^t\\
.\\.\\
\delta_n^tx_1^t & \delta_n^tx_2^t & ... &  \delta_n^tx_m^t\\
\end{bmatrix}
\end{equation}


式\eqref{xh1-7}是误差函数在t时刻对权重矩阵$U$的梯度。和权重矩阵$W$一样，最终的梯度也是各个时刻的梯度之和：
\[ \nabla_UE=\sum_{i=1}^t\nabla_{U_i}E \]

\section{RNN的梯度爆炸和消失问题}

不幸的是，实践中前面介绍的几种RNNs并不能很好的处理较长的序列。一个主要的原因是，RNN在训练中很容易发生梯度爆炸和梯度消失，这导致训练时梯度不能在较长序列中一直传递下去，从而使RNN无法捕捉到长距离的影响。

为什么RNN会产生梯度爆炸和消失问题呢？我们接下来将详细分析一下原因。我们根据式\eqref{xh1-3}可得：
\begin{align*}
\delta_k^T=&\delta_t^T\prod_{i=k}^{t-1}W\cdot diag[f'(\bm{net}_{i})]\\
\|\delta_k^T\|\leqslant&\|\delta_t^T\|\prod_{i=k}^{t-1}\|W\|\|diag[f'(\bm{net}_{i})]\|\\
\leqslant&\|\delta_t^T\|(\beta_W\beta_f)^{t-k}
\end{align*}

上式的$ \beta $定义为矩阵的模的上界。因为上式是一个指数函数，如果$t-k$很大的话（也就是向前看很远的时候），会导致对应的误差项的值增长或缩小的非常快，这样就会导致相应的梯度爆炸和梯度消失问题（取决于$ \beta $大于1还是小于1）。

通常来说，梯度爆炸更容易处理一些。因为梯度爆炸的时候，我们的程序会收到NaN错误。我们也可以设置一个梯度阈值，当梯度超过这个阈值的时候可以直接截取。

梯度消失更难检测，而且也更难处理一些。总的来说，我们有三种方法应对梯度消失问题：
\begin{itemize}
	\item 合理的初始化权重值。初始化权重，使每个神经元尽可能不要取极大或极小值，以躲开梯度消失的区域。
	\item 使用\texttt{relu}代替\texttt{sigmoid}和\texttt{tanh}作为激活函数。原理请参考上一篇文章零基础入门深度学习(4) - 卷积神经网络的激活函数一节。
	\item 使用其他结构的RNNs，比如长短时记忆网络（LTSM）和Gated Recurrent Unit（GRU），这是最流行的做法。我们将在以后的文章中介绍这两种网络。	
\end{itemize}
